//
// HTTPFormServer.cpp
//
// $Id: //poco/Main/Net/samples/HTTPFormServer/src/HTTPFormServer.cpp#5 $
//
// This sample demonstrates the HTTPServer and HTMLForm classes.
//
// Copyright (c) 2006, Applied Informatics Software Engineering GmbH.
// and Contributors.
//
// Permission is hereby granted, free of charge, to any person or organization
// obtaining a copy of the software and accompanying documentation covered by
// this license (the "Software") to use, reproduce, display, distribute,
// execute, and transmit the Software, and to prepare derivative works of the
// Software, and to permit third-parties to whom the Software is furnished to
// do so, all subject to the following:
//
// The copyright notices in the Software and this entire statement, including
// the above license grant, this restriction and the following disclaimer,
// must be included in all copies of the Software, in whole or in part, and
// all derivative works of the Software, unless such copies or derivative
// works are solely in the form of machine-executable object code generated by
// a source language processor.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//


#include <iostream>
#include <iterator>
#include <string>
#include <sstream>
#include <fstream>

#include "Poco/Net/HTTPServer.h"
#include "Poco/Net/HTTPRequestHandler.h"
#include "Poco/Net/HTTPRequestHandlerFactory.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTTPServerRequest.h"
#include "Poco/Net/HTTPServerResponse.h"
#include "Poco/Net/HTTPServerParams.h"
#include "Poco/Net/HTMLForm.h"
#include "Poco/Net/PartHandler.h"
#include "Poco/Net/MessageHeader.h"
#include "Poco/Net/ServerSocket.h"
#include "Poco/CountingStream.h"
#include "Poco/NullStream.h"
#include "Poco/StreamCopier.h"
#include "Poco/Exception.h"
#include "Poco/Util/ServerApplication.h"
#include "Poco/Util/Option.h"
#include "Poco/Util/OptionSet.h"
#include "Poco/Util/HelpFormatter.h"

#include "parser.h"
#include "propnet.h"

using Poco::Net::ServerSocket;
using Poco::Net::HTTPRequestHandler;
using Poco::Net::HTTPRequestHandlerFactory;
using Poco::Net::HTTPServer;
using Poco::Net::HTTPServerRequest;
using Poco::Net::HTTPServerResponse;
using Poco::Net::HTTPServerParams;
using Poco::Net::MessageHeader;
using Poco::Net::HTMLForm;
using Poco::Net::NameValueCollection;
using Poco::Util::ServerApplication;
using Poco::Util::Application;
using Poco::Util::Option;
using Poco::Util::OptionSet;
using Poco::Util::HelpFormatter;
using Poco::CountingInputStream;
using Poco::NullOutputStream;
using Poco::StreamCopier;



class ServerRequestHandler : public HTTPRequestHandler {
  public:
    ServerRequestHandler()
    {
    }

    void handleRequest(HTTPServerRequest &request, HTTPServerResponse &response)
    {
        bool playing = false; // state of the handler statemachine
        Application &app = Application::instance();
        app.logger().information("Request from " + request.clientAddress().toString());

        std::istream &istr = request.stream();
        // store the incoming request, word for word, in a vector<string>
        std::vector<Parser::Token> tokens = Parser::lexStream(istr);

        Parser::printTokens(tokens, std::cout);

        response.setChunkedTransferEncoding(true);
        response.setContentType("text/acl");
        std::ostream &ostr = response.send();

        if (tokens.size() < 3) { // shortest request is "(info)"
            ostr << "niks?\n";
            return;
        }

        if (tokens[0].type != Parser::TokenType::open) { // every request starts with a "(" and should end with one too
            ostr << "Frink capiert es nicht mehr, aborting\n";
            playing = false;
            return;
        }

        auto cmd = tokens[1].str;
        if (!cmd.compare("info")) {
            if (!playing) {
                ostr << "((name Frink_v0.0) (status available))\n";
            } else {
                ostr << "((name Frink_v0.0) (status busy))\n";
            }
        } else if (!cmd.compare("start") && (!playing)) {
            ostr << "(ready)\n";
            playing = true;
        } else if (!cmd.compare("play") && (playing)) {

        } else if (!cmd.compare("stop") && (playing)) {
            playing = false;
            ostr << "(done)\n";
        } else {
            // may be an "abort" command, or some other error
            ostr << "ABORT ABORT ABORT\n";
        }

        ostr.flush();
    }
};


class ServerRequestHandlerFactory : public HTTPRequestHandlerFactory {
  public:
    ServerRequestHandlerFactory()
    {
    }

    HTTPRequestHandler *createRequestHandler(const HTTPServerRequest &)
    {
        return new ServerRequestHandler;
    }
};


class FrinkServer : public Poco::Util::ServerApplication
                    /// The main application class.
                    ///
                    /// This class handles command-line arguments and
                    /// configuration files.
                    /// Start the HTTPFormServer executable with the help
                    /// option (/help on Windows, --help on Unix) for
                    /// the available command line options.
                    ///
                    /// To use the sample configuration file (HTTPFormServer.properties),
                    /// copy the file to the directory where the HTTPFormServer executable
                    /// resides. If you start the debug version of the HTTPFormServer
                    /// (HTTPFormServerd[.exe]), you must also create a copy of the configuration
                    /// file named HTTPFormServerd.properties. In the configuration file, you
                    /// can specify the port on which the server is listening (default
                    /// 9980) and the format of the date/Form string sent back to the client.
                    ///
                    /// To test the FormServer you can use any web browser (http://localhost:9980/).
                    {
  public:
    FrinkServer() : _helpRequested(false), _parseGDLFile("")
    {
        setUnixOptions(true); // require Unix-style ("--help") options, also on Windows
    }

    ~FrinkServer()
    {
    }

  protected:
    void initialize(Application &self)
    {
        loadConfiguration(); // load default configuration files, if present
        ServerApplication::initialize(self);
    }

    void uninitialize()
    {
        ServerApplication::uninitialize();
    }

    void defineOptions(OptionSet &options)
    {
        ServerApplication::defineOptions(options);

        options.addOption(Option("gdl", "g", "parse a (prefix) GDL file and output propnet graph")
                              .required(false)
                              .repeatable(false)
                              .argument("filename"));
        options.addOption(Option("port", "p", "TCP port to listen for games for")
                              .required(false)
                              .repeatable(false)
                              .argument("port number"));
        options.addOption(Option("help", "h", "display help information on command line arguments")
                              .required(false)
                              .repeatable(false));
    }

    void handleOption(const std::string &name, const std::string &value)
    {
        ServerApplication::handleOption(name, value);

        if (name == "help")
            _helpRequested = true;
        else if (name == "port")
            config().setInt("HTTPServer.port", stoi(value));
        else if (name == "gdl")
            _parseGDLFile = value;
    }

    void displayHelp()
    {
        HelpFormatter helpFormatter(options());
        helpFormatter.setCommand(commandName());
        helpFormatter.setUsage("OPTIONS");
        helpFormatter.setHeader("A general game player, that waits for HTTP requests from the game manager.");
        helpFormatter.format(std::cout);
    }

    void loadFile(std::string filename, std::ifstream &fstr)
    {
        fstr.open(filename);
    }

    int main(const std::vector<std::string> & /* args */)
    {
        Application &app = Application::instance();

        if (_helpRequested) {
            displayHelp();
        } else if (_parseGDLFile.length() > 0) {
            std::ifstream filestr;
            loadFile(_parseGDLFile, filestr);
            if (!filestr.good()) {
                app.logger().error("Error opening GDL file");
                return Application::EXIT_NOINPUT;
            }

            auto gdl_tokens = Parser::lexStream(filestr);
            if (gdl_tokens.size() < 3) {
                app.logger().error("Error parsing GDL file");
                return Application::EXIT_DATAERR;
            }
            //Parser::printTokens(gdl_tokens, std::cout);

            PropNet::PropNet propnet;
            propnet.loadGDL(gdl_tokens);
            propnet.writeDOT(_parseGDLFile + ".dot");
        } else {
            unsigned short port = (unsigned short)config().getInt("HTTPServer.port", 9147);

            // set-up a server socket
            ServerSocket svs(port);
            // set-up a HTTPServer instance
            HTTPServer srv(new ServerRequestHandlerFactory, svs, new HTTPServerParams);
            // start the HTTPServer
            srv.start();
            // wait for CTRL-C or kill
            waitForTerminationRequest();
            // Stop the HTTPServer
            srv.stop();
        }
        return Application::EXIT_OK;
    }

  private:
    bool _helpRequested;
    std::string _parseGDLFile;
};


int main(int argc, char **argv)
{
    FrinkServer app;
    return app.run(argc, argv);
}
